/*
 * @Descripttion:
 * @Author: ziwei
 * @Date: 2022-07-03 20:33:56
 * @LastEditors: ziwei
 * @LastEditTime: 2022-07-04 12:19:08
 */
//装饰器：特殊的声明，类装饰器属性装饰器
export {};
// class Person{
//     say(){}
// }
// function Person(){}
// Object.defineProperties(Person.prototype,'say',{
//     value:function say(){}
// })
// })
//类装饰器:类声明之前声明，监视修改替换类的定义
//类装饰器工厂
namespace a {
  //命名空间
  function addNameEatFactory(name:string) {
    return function addNameEat(x: Function) {
      x.prototype.name = name;
      x.prototype.eat = function () {};
    };
  }
  @addNameEatFactory("bb")
  class Person {
    name: string;
    eat: Function;
    constructor() {}
  }
  let p: Person = new Person();
  console.log(p.name);
  p.eat();
}

namespace b {
  function addNameEat(x: Function) {
    x.prototype.name = "xx";
    x.prototype.eat = function () {};
  }
  @addNameEat
  class Person {
    name: string;
    eat: Function;
    constructor() {}
  }
//   let p: Person = new Person();
//   console.log(p.name);
//   p.eat();
}

//替换类:属性可以多但不能少
//类型安全
namespace c {
    function replaceClass(constructor:Function) {
      return class {
        name: string;
        eat: Function;
        constructor() {}
      }
    }
    @replaceClass
    class Person {
      name: string;
      eat: Function;
      constructor() {}
    }
    // let p: Person = new Person();
    // console.log(p.name);
    // p.eat();
  }

//属性装饰器
//装饰属性 
//装饰方法
  namespace d {
    //如果装饰的是实例属性，target是构造函数的原型
    function upperCase(target:any,propertyKey:string){
        let value = target[propertyKey]
        const getter = ()=>value
        const setter = (newVal:string)=>{value=newVal.toUpperCase()}
        if(delete target[propertyKey]){
            Object.defineProperty(target,propertyKey,{
                get:getter,
                set:setter,
                enumerable:true,
                configurable:true
            })
        }
    }
    //如果装饰的是静态属性，target是构造函数的本身
    function staticPropertyDecorator(target:any,propertyKey:string){
        console.log(target,propertyKey);   
    }
    function noEnumerable(target:any,propertyKey:string,descriptor:PropertyDescriptor){
        console.log(target);
        console.log(propertyKey);
        descriptor.enumerable =false
    }
    function toNumber(target:any,propertyKey:string,descriptor:PropertyDescriptor){
       let oldMethod  = descriptor.value
       descriptor.value = function(...args:any[]){
        args = args.map(item=>parseFloat(item))
        return oldMethod.apply(this,args)
       }
    }
    class Person{
        @upperCase
        name:string = 'xx';//实例属性
        @staticPropertyDecorator
        public static age:number = 10;//静态属性
        @noEnumerable //方法装饰器
        getName(){//实例方法
            console.log(this.name); 
        }
        @toNumber
        sum(...args:any[]){//实例方法
            return args.reduce((accu:number,item:number)=>accu+item,0)
        }
    }


    let p = new Person();
    console.log(p.name);
    console.log(p.sum('1','2','3'));
  }

  //参数装饰器
  namespace e{
    //在IOC容器里大放异彩 Next.js大量用到了参数装饰器
    //target 静态成员就是构造函数 非静态成员就是构造函数原型，方法的名称，参数的索引
    function addAge(target:any,methodName,paramIndex:number){
      console.log(target,methodName,paramIndex);
      target.age = 10;
    }
    class Person{
        age:number
        login(username:string,@addAge password:string){
            console.log(this.age,username,password);     
        }
    }
    let p = new Person()
    p.login('1','2')
  }
//装饰器的执行顺序
  namespace f{
    function ClassDecorator1(){
      return function(target){
        console.log("ClassDecorator1");
        
      }
    }
    function ClassDecorator2(){
      return function(target){
        console.log("ClassDecorator2");
      }
    }
    function PropertyDecorator(name:string){
      return function(target,propertyName){
        console.log("PropertyDecorator",propertyName);
      }
    }
    function MethodDecorator(){
      return function(target,propertyName){
        console.log("MethodDecorator",propertyName,name);
      }
    }
    function ParameterDecorator(){
      return function(target,methodName,index){
        console.log("ParameterDecorator",methodName,index);
      }
    }
    @ClassDecorator1()
    @ClassDecorator2()
    class Person{
      @PropertyDecorator('name')
      name:string = ''
      @PropertyDecorator('age')
      age:number = 10;
      @MethodDecorator()
      hello(@ParameterDecorator() p1:string,@ParameterDecorator() p2:string){

      }
    }
  }

  //执行顺序
  /**
   * 1.类装饰器是最后执行的，后写的类的装饰器先执行
   * 2.方法和方法参数重的装饰器，先执行参数装饰器
   * 3.方法和属性装饰器，谁在前面先执行谁
   * 
   * 一般从内往外执行：先内后外、
   * 类别组件 componentDidMount 先上后下 先内后外
   * 知识的嫁接
   * koa redux中间件
   */